#include "application.h"

/* global varaible */
INT32U fps_decode_avi_video;
INT32U fps_decode_avi_audio_dac_start;
INT32U fps_decode_avi_audio_dac_end;
static vid_dec_buf_struct	g_audio;
static DMA_STRUCT			g_aud_dec_dma_dbf;

#define VIDEO_DECODER_JPEG_NUM	JPEG_DECODE_DEV_NUM

// timer and sync
void vid_dec_timer_isr(void)
{
	if(p_vid_dec_para->audio_flag)
	{
		if((p_vid_dec_para->tv - p_vid_dec_para->ta) < p_vid_dec_para->time_range)
		{
			p_vid_dec_para->tv += p_vid_dec_para->tick_time2;
		}

		if(p_vid_dec_para->ta - p_vid_dec_para->tv > p_vid_dec_para->time_range)
		{
			// Make video faster if ta is greater tv more than 32ms
			// This branch will only occur when ICE stop on debug point
			p_vid_dec_para->tv += p_vid_dec_para->tick_time2;
		}
	}
	else
	{
		p_vid_dec_para->tv += p_vid_dec_para->tick_time2;
	}

	if((p_vid_dec_para->tv - p_vid_dec_para->Tv) >= 0)
	{
		if(p_vid_dec_para->post_cnt == p_vid_dec_para->pend_cnt)
		{
			OSQPost(vid_dec_q, (void *) MSG_VID_DEC_ONE_FRAME);
			p_vid_dec_para->post_cnt++;
		}
	}
}

void vid_dec_stop_timer(void)
{
	timer_stop(VIDEO_DECODE_TIMER);
}

void vid_dec_start_timer(void)
{
	INT32U	temp, freq_hz;

	if(p_vid_dec_para->audio_flag)
	{
		//temp = 0x10000 -((0x10000 - (R_TIMERE_PRELOAD & 0xFFFF)) * p_vid_dec_para->n);
		temp = (0x10000 - (R_TIMERE_PRELOAD & 0xFFFF)) * p_vid_dec_para->n;
		freq_hz = SYS_CLK / 2 / temp;
		if(SYS_CLK % (2 * temp))
			freq_hz++;
	}
	else
		freq_hz = TIME_BASE_TICK_RATE;

	timer_freq_setup(VIDEO_DECODE_TIMER, freq_hz, 0, vid_dec_timer_isr);
}

//memory
static void vid_dec_buffer_number_init(void)
{
	gp_memset((INT8S *) &g_audio, 0x00, sizeof(vid_dec_buf_struct));

	//audio
	g_audio.total_number = AUDIO_FRAME_NO;
}

static INT32S vid_dec_audio_memory_alloc(void)
{
	INT32S	i, nRet;

	if(p_vid_dec_para->audio_flag == 0)
		RETURN(0);

	for(i = 0; i < g_audio.total_number; i++)
	{
		if(p_vid_dec_para->audio_decode_addr[i] == 0)
		{
			p_vid_dec_para->audio_decode_addr[i] = (INT32U) gp_malloc_align(p_vid_dec_para->aud_frame_size, 32);
			if(p_vid_dec_para->audio_decode_addr[i] == 0)
				RETURN(-1);
			gp_memset((INT8S *) p_vid_dec_para->audio_decode_addr[i], 0x80, p_vid_dec_para->aud_frame_size);
		}
	}

	nRet = STATUS_OK;
Return:
	for(i = 0; i < g_audio.total_number; i++)
		DBG_PRINT("AudioFrame = 0x%x\r\n", p_vid_dec_para->audio_decode_addr[i]);

	return nRet;
}

static void vid_dec_audio_memory_free(void)
{
	INT32S	i;

	for(i = 0; i < g_audio.total_number; i++)
	{
		if(p_vid_dec_para->audio_decode_addr[i])
		{
			gp_free((void *) p_vid_dec_para->audio_decode_addr[i]);
			p_vid_dec_para->audio_decode_addr[i] = 0;
		}
	}
}

INT32S vid_dec_memory_alloc(void)
{
	//init index
	vid_dec_buffer_number_init();

	if(vid_dec_audio_memory_alloc() < 0)
		return -1;
	return 0;
}

void vid_dec_memory_free(void)
{
	vid_dec_audio_memory_free();
}

INT32U vid_dec_get_next_aud_buffer(void)
{
	INT32U	addr;

	//do{
	addr = p_vid_dec_para->audio_decode_addr[g_audio.current_index++];
	if(g_audio.current_index >= g_audio.total_number)
		g_audio.current_index = 0;

	//}while(addr == g_lock_vid_addr);
	return addr;
}

//status
void vid_dec_set_status(INT32S flag)
{
	p_vid_dec_para->status |= flag;
}

void vid_dec_clear_status(INT32S flag)
{
	p_vid_dec_para->status &= ~flag;
}

INT32S vid_dec_get_status(void)
{
	return p_vid_dec_para->status;
}

//video info
void vid_dec_set_video_flag(INT8S video_flag)
{
	if(video_flag)
		p_vid_dec_para->video_flag = TRUE;
	else
		p_vid_dec_para->video_flag = FALSE;
}

INT8S vid_dec_get_video_flag(void)
{
	return p_vid_dec_para->video_flag;
}

INT32S vid_dec_get_video_format(INT32U biCompression)
{
	INT8U	data[4];

	data[0] = (biCompression >> 0) & 0xFF;	//X
	data[1] = (biCompression >> 8) & 0xFF;	//X
	data[2] = (biCompression >> 16) & 0xFF; //X
	data[3] = (biCompression >> 24) & 0xFF; //X
	if((data[0] == 'X' || data[0] == 'x') && (data[1] == 'V' || data[1] == 'v') && (data[2] == 'I' || data[2] == 'i') && (data[3] == 'D' || data[3] == 'd'))
	{
		DBG_PRINT("VidFormat = C_XVID_FORMAT\r\n");
		return C_XVID_FORMAT;
	}
	else
	if((data[0] == 'M' || data[0] == 'm') && (data[1] == '4') && (data[2] == 'S' || data[2] == 's') && (data[3] == '2'))
	{
		DBG_PRINT("VidFormat = C_M4S2_FORMAT\r\n");
		return C_M4S2_FORMAT;
	}
	else
	if((data[0] == 'H' || data[0] == 'h') && (data[1] == '2') && (data[2] == '6') && (data[3] == '3'))
	{
		DBG_PRINT("VidFormat = C_H263_FORMAT\r\n");
		return C_H263_FORMAT;
	}
	else
	if((data[0] == 'M' || data[0] == 'm') && (data[1] == 'J' || data[1] == 'j') && (data[2] == 'P' || data[2] == 'p') && (data[3] == 'G' || data[3] == 'g'))
	{
		DBG_PRINT("VidFormat = C_MJPG_FORMAT\r\n");
		return C_MJPG_FORMAT;
	}

	DBG_PRINT("NotSupportVidFormat = 0x%x\r\n", biCompression);
	return STATUS_FAIL;
}

INT32S vid_dec_get_file_format(INT8S *pdata)
{
	if((*(pdata + 0) == 'A' || *(pdata + 0) == 'a') && (*(pdata + 1) == 'V' || *(pdata + 1) == 'v') && (*(pdata + 2) == 'I' || *(pdata + 2) == 'i'))
	{
		return FILE_TYPE_AVI;
	}
	else
	if((*(pdata + 0) == 'M' || *(pdata + 0) == 'm') && (*(pdata + 1) == 'O' || *(pdata + 1) == 'o') && (*(pdata + 2) == 'V' || *(pdata + 2) == 'v'))
	{
		return FILE_TYPE_MOV;
	}
	else
	if((*(pdata + 0) == 'M' || *(pdata + 0) == 'm') && (*(pdata + 1) == 'P' || *(pdata + 1) == 'p') && (*(pdata + 2) == '4' || *(pdata + 2) == '4'))
	{
		return FILE_TYPE_MOV;
	}
	else
	if((*(pdata + 0) == '3' || *(pdata + 0) == '3') && (*(pdata + 1) == 'G' || *(pdata + 1) == 'g') && (*(pdata + 2) == 'P' || *(pdata + 2) == 'p'))
	{
		return FILE_TYPE_MOV;
	}
	else
	if((*(pdata + 0) == 'M' || *(pdata + 0) == 'm') && (*(pdata + 1) == '4' || *(pdata + 1) == '4') && (*(pdata + 2) == 'A' || *(pdata + 2) == 'a'))
	{
		return FILE_TYPE_MOV;
	}

	return STATUS_FAIL;
}

void vid_dec_get_size(INT16U *width, INT16U *height)
{
	*width = p_bitmap_info->biWidth;
	*height = p_bitmap_info->biHeight;
}

//audio info
void vid_dec_set_audio_flag(INT8S audio_flag)
{
	if(audio_flag)
		p_vid_dec_para->audio_flag = TRUE;
	else
		p_vid_dec_para->audio_flag = FALSE;
}

INT8S vid_dec_get_audio_flag(void)
{
	return p_vid_dec_para->audio_flag;
}

//audio decoder
INT32S vid_dec_set_aud_dec_work_mem(INT32U work_mem_size)
{
	p_vid_dec_para->work_mem_size = work_mem_size;
	p_vid_dec_para->work_mem_addr = (INT8U *) gp_malloc_align(work_mem_size, 4);
	if(!p_vid_dec_para->work_mem_addr)
		return -1;

	gp_memset((INT8S *) p_vid_dec_para->work_mem_addr, 0, work_mem_size);
	return 0;
}

INT32S vid_dec_set_aud_dec_ring_buffer(void)
{
	p_vid_dec_para->ring_buffer_size = MultiMediaParser_GetAudRingLen(p_vid_dec_para->media_handle);
	p_vid_dec_para->ring_buffer_addr = (INT8U *) MultiMediaParser_GetAudRing(p_vid_dec_para->media_handle);
	if(!p_vid_dec_para->ring_buffer_addr)
		return -1;

	return 0;
}

void vid_dec_aud_dec_memory_free(void)
{
	if(p_vid_dec_para->work_mem_addr)
	{
		gp_free((void *) p_vid_dec_para->work_mem_addr);
	}

	p_vid_dec_para->work_mem_size = 0;
	p_vid_dec_para->work_mem_addr = 0;
	p_vid_dec_para->ring_buffer_size = 0;
	p_vid_dec_para->ring_buffer_addr = 0;
}

//audio dac
INT32S aud_dec_double_buffer_put(INT16U *pdata, INT32U cwlen, OS_EVENT *os_q)
{
	INT32S	status;

	g_aud_dec_dma_dbf.s_addr = (INT32U) pdata;
	g_aud_dec_dma_dbf.t_addr = (INT32U) P_DAC_CHA_DATA;
	g_aud_dec_dma_dbf.width = DMA_DATA_WIDTH_2BYTE;
	g_aud_dec_dma_dbf.count = (INT32U) cwlen;
	g_aud_dec_dma_dbf.notify = NULL;
	g_aud_dec_dma_dbf.timeout = 0;

	status = drv_l1_dma_transfer_with_double_buf(&g_aud_dec_dma_dbf, os_q);
	if(status != 0)
	{
		return status;
	}

	++fps_decode_avi_audio_dac_start;

	return STATUS_OK;
}

INT32U aud_dec_double_buffer_set(INT16U *pdata, INT32U cwlen)
{
	INT32S	status;

	g_aud_dec_dma_dbf.s_addr = (INT32U) pdata;
	g_aud_dec_dma_dbf.t_addr = (INT32U) P_DAC_CHA_DATA;
	g_aud_dec_dma_dbf.width = DMA_DATA_WIDTH_2BYTE;
	g_aud_dec_dma_dbf.count = (INT32U) cwlen;
	g_aud_dec_dma_dbf.notify = NULL;
	g_aud_dec_dma_dbf.timeout = 0;
	status = drv_l1_dma_transfer_double_buf_set(&g_aud_dec_dma_dbf);
	if(status != 0)
		return status;

	return STATUS_OK;
}

INT32S aud_dec_dma_status_get(void)
{
	if(g_aud_dec_dma_dbf.channel == 0xff)
		return -1;

	return drv_l1_dma_status_get(g_aud_dec_dma_dbf.channel);
}

void aud_dec_double_buffer_free(void)
{
	drv_l1_dma_transfer_double_buf_free(&g_aud_dec_dma_dbf);
	g_aud_dec_dma_dbf.channel = 0xff;
	++fps_decode_avi_audio_dac_end;
}

//dac init
void aud_dec_dac_start(INT8U channel_no, INT32U sample_rate)
{
	if(p_wave_info->wFormatTag == WAVE_FORMAT_MPEGLAYER3)	// 2 channel
		dac_stereo_set();
	else
	if(channel_no == 1)
		dac_mono_set();
	else
		dac_stereo_set();

	dac_sample_rate_set(sample_rate);
}

void aud_dec_dac_stop(void)
{
	dac_timer_stop();
}

void aud_dec_ramp_down_handle(INT8U channel_no)
{
	INT16S	last_ldata, last_rdata;
	INT16S	i, temp;

	temp = 0 - DAC_RAMP_DOWN_STEP;
	last_ldata = R_DAC_CHA_DATA;
	last_rdata = R_DAC_CHB_DATA;

	//unsigned to signed
	last_ldata ^= 0x8000;
	last_rdata ^= 0x8000;

	return;

	//change timer to 44100
	dac_sample_rate_set(44100);
	while(1)
	{
		if(last_ldata > 0x0)
		{
			last_ldata -= DAC_RAMP_DOWN_STEP;
		}
		else
		if(last_ldata < 0x0)
		{
			last_ldata += DAC_RAMP_DOWN_STEP;
		}

		if((last_ldata < DAC_RAMP_DOWN_STEP) && (last_ldata > temp))
		{
			last_ldata = 0;
		}

		if(channel_no == 2)
		{
			if(last_rdata > 0x0)
			{
				last_rdata -= DAC_RAMP_DOWN_STEP;
			}
			else
			if(last_rdata < 0x0)
			{
				last_rdata += DAC_RAMP_DOWN_STEP;
			}

			if((last_rdata < DAC_RAMP_DOWN_STEP) && (last_rdata > temp))
			{
				last_rdata = 0;
			}
		}

		for(i = 0; i < DAC_RAMP_DOWN_STEP_HOLD; i++)
		{
			if(channel_no == 2)
			{
				while(R_DAC_CHA_FIFO & 0x8000);
				R_DAC_CHA_DATA = last_ldata;
				while(R_DAC_CHB_FIFO & 0x8000);
				R_DAC_CHB_DATA = last_rdata;
			}
			else
			{
				while(R_DAC_CHA_FIFO & 0x8000);
				R_DAC_CHA_DATA = last_ldata;
			}
		}

		if(channel_no == 2)
		{
			if((last_ldata == 0x0) && (last_rdata == 0x0))
				break;
		}
		else
		{
			if(last_ldata == 0x0)
				break;
		}
	}

	dac_timer_stop();
}

//
// Frame mode decoded,
// frame addr : data_addr
// frame size : data_size
//
void initial_GP420_black_block(INT8U *buf , INT8U block_w, INT32U output_w)
{
	INT8U i;//,j;

	for(i=0;i<block_w;i++)
	{
		*buf = 0; // Y value
		buf++;
	}

	buf+=(output_w-(INT32U)block_w);
	for(i=0;i<block_w;i++)
	{
		*buf=0x80; // UV value
		buf++;
	}

	buf+=(output_w-(INT32U)block_w);
	for(i=0;i<block_w;i++)
	{
		*buf = 0; // Y value
		buf++;
	}
}

#define				VID_DECODE_Q_MAX	4
void*				vid_decode_q_stack[VID_DECODE_Q_MAX];
static OS_EVENT*	vid_decode_q;

enum
{
	DECODE_SCALER_INPUT_EMPTY	= 0x0001,
	DECODE_SCALER_OUTPUT_DONE	= 0x0002,
	DECODE_JPG_INPUT_EMPTY		= 0x0010,
	DECODE_JPG_DECODE_DONE		= 0x0020,
	DECODE_JPG_OUTPUT_FULL		= 0x0040,
	DECODE_JPG_STATUS_STOP		= 0x0080,
	DECODE_JPG_TIMEOUT			= 0x0100,
	DECODE_JPG_INIT_ERR			= 0x0200,
	DECODE_JPG_RST_VLC_DONE		= 0x0400,
	DECODE_JPG_RST_MARKER_ERR	= 0x0800
};

static void video_decode_ps_isr_scale(PS_ID ps, PS_ST PScaler_Event)
{
	INT8U	err;
	INT32U	st = 0;

	st |= (PScaler_Event & PIPELINE_SCALER_STATUS_FRAME_DONE)	? DECODE_SCALER_OUTPUT_DONE	: 0;
	st |= (PScaler_Event & PIPELINE_SCALER_STATUS_INPUT_EMPTY)	? DECODE_SCALER_INPUT_EMPTY	: 0;

	if(st)
	{
		err = OSQPost(vid_decode_q, (void *) st);
		if(err != OS_NO_ERR)
		{
			DBG_PRINT("%s:%d OSQPost err=%d\r\n", __func__, __LINE__, err);
		}
	}
}

static void video_decode_jpg_isr_decode(INT32S eventMsg, INT32U vlcCount)
{
	INT8U	err;
	INT32U	st = 0;

	st |= (eventMsg & C_JPG_STATUS_DECODE_DONE)		? DECODE_JPG_DECODE_DONE	: 0;
	st |= (eventMsg & C_JPG_STATUS_OUTPUT_FULL)		? DECODE_JPG_OUTPUT_FULL	: 0;
	st |= (eventMsg & C_JPG_STATUS_INPUT_EMPTY)		? DECODE_JPG_INPUT_EMPTY	: 0;
	st |= (eventMsg & C_JPG_STATUS_STOP)			? DECODE_JPG_STATUS_STOP	: 0;
	st |= (eventMsg & C_JPG_STATUS_TIMEOUT)			? DECODE_JPG_TIMEOUT		: 0;
	st |= (eventMsg & C_JPG_STATUS_INIT_ERR)		? DECODE_JPG_INIT_ERR		: 0;
	st |= (eventMsg & C_JPG_STATUS_RST_VLC_DONE)	? DECODE_JPG_RST_VLC_DONE	: 0;
	st |= (eventMsg & C_JPG_STATUS_RST_MARKER_ERR)	? DECODE_JPG_RST_MARKER_ERR	: 0;

	if(st)
	{
		err = OSQPost(vid_decode_q, (void *) st);
		if(err != OS_NO_ERR)
		{
			DBG_PRINT("%s:%d OSQPost err=%d\r\n", __func__, __LINE__, err);
		}
	}
}

INT32S video_decode_jpeg_process(
	INT32U buf_addr, INT32U jpeg_size
	, INT32U jpeg_out_buf, INT32U jpeg_out_buf_b
	, INT32S (*data_reload)(INT32U,INT32U), INT32U reload_size)
{
	INT32U	vid_decode_st;
	INT32U	jpeg_vlc_addr, jpeg_header_len;
	INT32U	len_jpeg_dec, total_jpeg_dec;
	INT8U	scaler_start = 0;
	INT8U	jpeg_out_buf_idx;

	if(!vid_decode_q)
	{
		vid_decode_q = OSQCreate(vid_decode_q_stack, VID_DECODE_Q_MAX);
	}
	else
	{
		OSQFlush(vid_decode_q);
	}

	jpeg_decode_callback_register(video_decode_jpg_isr_decode);
	drv_l1_pscaler_callback_register(PSCALER_A, video_decode_ps_isr_scale);

	jpeg_vlc_addr = (INT32U)jpeg_decode_image_vlc_addr_get();
	jpeg_header_len = jpeg_vlc_addr - buf_addr;

	total_jpeg_dec = jpeg_header_len;

	if(reload_size && (reload_size < jpeg_size))
	{
		len_jpeg_dec = reload_size - jpeg_header_len;
	}
	else
	{
		len_jpeg_dec = jpeg_size - jpeg_header_len;
	}

	if(jpeg_decode_on_the_fly_start((INT8U*)(buf_addr + total_jpeg_dec), len_jpeg_dec) != STATUS_OK)
	{
		return STATUS_FAIL;
	}

	total_jpeg_dec += len_jpeg_dec;

	jpeg_out_buf_idx = 0;
	vid_decode_st = DECODE_SCALER_INPUT_EMPTY;
	while(1)
	{
		INT8U err;

		vid_decode_st |= (INT32U) OSQPend(vid_decode_q, 100, &err);

		if(err != OS_NO_ERR)
		{
			DBG_PRINT("%s:%d OSQPend err=%d\r\n", __func__, __LINE__, err);
			return STATUS_FAIL;
		}

		if((vid_decode_st & DECODE_JPG_DECODE_DONE)
			&& (vid_decode_st & DECODE_SCALER_OUTPUT_DONE)
		)
		{
			return STATUS_OK;
		}

		if(vid_decode_st & DECODE_JPG_INPUT_EMPTY)
		{
			vid_decode_st &= ~DECODE_JPG_INPUT_EMPTY;

			if(data_reload && reload_size)
			{
				if((jpeg_size - total_jpeg_dec) > reload_size)
				{
					len_jpeg_dec = reload_size;
				}
				else
				{
					len_jpeg_dec = jpeg_size - total_jpeg_dec;
				}

				if(len_jpeg_dec)
				{
					if(data_reload(buf_addr, len_jpeg_dec) < 0)
					{
						DBG_PRINT("video_decode_jpeg_read_partial_process read jpeg file fail.\r\n");
						return STATUS_FAIL;
					}
				}
			}
			else
			{
				if(jpeg_size > total_jpeg_dec)
				{
					len_jpeg_dec = jpeg_size - total_jpeg_dec;
				}
				else
				{
					len_jpeg_dec = 0;
				}
			}

			if(jpeg_decode_on_the_fly_start((INT8U*)buf_addr, len_jpeg_dec) != STATUS_OK)
			{
				return STATUS_FAIL;
			}

			total_jpeg_dec += len_jpeg_dec;
		}

		if((vid_decode_st & (DECODE_JPG_OUTPUT_FULL | DECODE_JPG_DECODE_DONE))
			&& (vid_decode_st & (DECODE_SCALER_INPUT_EMPTY | DECODE_SCALER_OUTPUT_DONE))
		)
		{
			if(jpeg_out_buf_idx)
			{
				drv_l1_pscaler_input_buffer_set(PSCALER_A, jpeg_out_buf_b);
			}
			else
			{
				drv_l1_pscaler_input_buffer_set(PSCALER_A, jpeg_out_buf);
			}

			if(!scaler_start)
			{
				scaler_start = 1;
				drv_l1_pscaler_interrupt_enable(PSCALER_A, 1);
				drv_l1_pscaler_start(PSCALER_A);
			}
			else
			if(vid_decode_st & DECODE_SCALER_INPUT_EMPTY)
			{
				drv_l1_pscaler_input_fifo_restart(PSCALER_A);
			}

			if(vid_decode_st & DECODE_JPG_OUTPUT_FULL)
			{
				jpeg_out_buf_idx ^= 1;
				if(jpeg_decode_output_restart())
				{
					DBG_PRINT("Failed to call jpeg_decode_output_restart()\r\n");
					return STATUS_FAIL;
				}

				vid_decode_st &= ~DECODE_JPG_OUTPUT_FULL;
			}

			vid_decode_st &= ~DECODE_SCALER_INPUT_EMPTY;
		}

		if(vid_decode_st & DECODE_JPG_STATUS_STOP)
		{
			DBG_PRINT("JPEG is not started!\r\n");
			return STATUS_FAIL;
		}

		if(vid_decode_st & DECODE_JPG_TIMEOUT)
		{
			DBG_PRINT("JPEG execution timeout!\r\n");
			return STATUS_FAIL;
		}

		if(vid_decode_st & DECODE_JPG_INIT_ERR)
		{
			DBG_PRINT("JPEG init error!\r\n");
			return STATUS_FAIL;
		}

		if(vid_decode_st & DECODE_JPG_RST_VLC_DONE)
		{
			DBG_PRINT("JPEG Restart marker number is incorrect!\r\n");
			return STATUS_FAIL;
		}

		if(vid_decode_st & DECODE_JPG_RST_MARKER_ERR)
		{
			DBG_PRINT("JPEG Restart marker sequence error!\r\n");
			return STATUS_FAIL;
		}
	}		// while loop of JPEG decode

	return STATUS_OK;
}
#if 0
INT32S video_decode_jpeg(
	INT8U *data_addr, INT32U data_size
	, INT32U output_addr, INT32U output_width, INT32U output_height
	, INT32S (*data_reload)(INT32U,INT32U), INT32U reload_size, INT8U flag)

{

	INT16U	img_valid_width, img_valid_height, img_extend_width, img_extend_height;
	INT32U	jpg_temp_out_buf;
	INT32S	ret;
	INT16U	i;
	INT32U	fifo_line = 16;
	INT32U	scale_in_w, scale_in_h;
	INT32U	scale_out_w, scale_out_h;
	INT32U	scale_factor_w, scale_factor_h;
	INT16U	outfull_start_x = 0;
	INT16U	outfull_start_y = 0;
	INT8U	h_first;
	INT8U	wIntegration, hIntegration;

	// Find JPEG header(0xFFD8)
	for(i = 0; i < 64; i++)
	{
		if(*data_addr == 0xFF)
		{
			break;
		}
		else
		{
			data_addr++;
		}
	}

	// Parse JPEG data header
	jpeg_decode_init();

	if(jpeg_decode_parse_header(data_addr, data_size) != JPEG_PARSE_OK)
	{
		return STATUS_FAIL;
	}

	img_valid_width = jpeg_decode_image_width_get();
	img_valid_height = jpeg_decode_image_height_get();
	img_extend_width = jpeg_decode_image_extended_width_get();
	img_extend_height = jpeg_decode_image_extended_height_get();

	/*
	if (img_valid_width >= output_width*4 && img_valid_height >= output_height*4)
	{
		drv_l1_jpeg_decode_scale_down_set(JPEG_DECODE_DEV_NUM, ENUM_JPG_DIV4);
		img_extend_width	>>= 2;
		img_extend_height	>>= 2;
		img_valid_width 	>>= 2;
		img_valid_height	>>= 2;
	}
	else
	if (img_valid_width >= output_width*2 && img_valid_height >= output_height*2)
	{
		drv_l1_jpeg_decode_scale_down_set(JPEG_DECODE_DEV_NUM, ENUM_JPG_DIV2);
		img_extend_width	>>= 1;
		img_extend_height	>>= 1;
		img_valid_width 	>>= 1;
		img_valid_height	>>= 1;
	} */

	if(fifo_line)
	{
		jpg_temp_out_buf = (INT32U) gp_malloc_align(img_extend_width * fifo_line << 2, 64);
	}
	else
	{
		jpg_temp_out_buf = (INT32U) gp_malloc_align(img_extend_width * img_extend_height << 1, 64);
	}

	if(!jpg_temp_out_buf)
	{
		DBG_PRINT("%s allocate fail\r\n", __func__);
		return STATUS_FAIL;
	}

	if(jpeg_decode_output_set(jpg_temp_out_buf, 0x0, 0x0, fifo_line))
	{
		gp_free((void *) jpg_temp_out_buf);
		return STATUS_FAIL;
	}

	drv_l1_jpeg_vlc_maximum_length_set(JPEG_DECODE_DEV_NUM, data_size);

	scale_in_w = img_valid_width;
	scale_in_h = img_valid_height;
	scale_out_w = output_width;
	scale_out_h = output_height;

#if defined(C_BROWSE_FULL_SCREEN) && C_BROWSE_FULL_SCREEN == CUSTOM_OFF
	scale_factor_w = ((scale_in_w << 16) / scale_out_w);
	if((scale_in_w << 16) % scale_out_w) ++scale_factor_w;

	scale_factor_h = ((scale_in_h << 16) / scale_out_h);
	if((scale_in_h << 16) % scale_out_h) ++scale_factor_h;

	/* resize image and relocate to center*/
	if(scale_factor_h < scale_factor_w)
	{
		INT16U tmp_start_h;
		INT16U tmp_width_h;
		INT32U tmp_factor_h;

		tmp_factor_h	= scale_factor_w;
		tmp_width_h 	= (scale_in_h << 16) / tmp_factor_h;

		if(tmp_width_h & 0x1)
		{
			tmp_width_h &= ~0x1;

			tmp_factor_h = ((scale_in_h << 16) / tmp_width_h);
			if((scale_in_h << 16) % tmp_width_h) ++tmp_factor_h;
		}

		tmp_start_h = (output_height - tmp_width_h) >> 1;
		tmp_start_h &= ~0x1;

		if(tmp_start_h > (output_height >> 4))
		{
			outfull_start_y = tmp_start_h;
			scale_factor_h	= tmp_factor_h;
			scale_out_h 	= tmp_width_h;

		#if 0
			DBG_PRINT("resize to %dx%d at (%d,%d)\r\n", scale_out_w, scale_out_h, outfull_start_x, outfull_start_y);
		#endif
		}

	}
	else
	if(scale_factor_w < scale_factor_h)
	{
		INT16U	tmp_start_w;
		INT16U	tmp_width_w;
		INT32U	tmp_factor_w;

		tmp_factor_w	= scale_factor_h;
		tmp_width_w 	= (scale_in_w << 16) / tmp_factor_w;

		if(tmp_width_w & 0xF)
		{
			tmp_width_w &= ~0xF;

			tmp_factor_w = ((scale_in_w << 16) / tmp_width_w);
			if((scale_in_w << 16) % tmp_width_w) ++tmp_factor_w;
		}

		tmp_start_w = (output_width - tmp_width_w) >> 1;
		tmp_start_w &= ~0xF;

		if(tmp_start_w > (output_width >> 4))
		{
			outfull_start_x = tmp_start_w;
			scale_factor_w	= tmp_factor_w;
			scale_out_w 	= tmp_width_w;

		#if 0
			DBG_PRINT("resize to %dx%d at (%d,%d)\r\n", scale_out_w, scale_out_h, outfull_start_x, outfull_start_y);
		#endif
		}
	}

#else
	if(scale_out_w >= scale_in_w)
	{
		wIntegration = 0;
		scale_factor_w = (scale_in_w << 16) / scale_out_w;
		if((scale_in_w << 16) % scale_out_w) ++scale_factor_w;
	}
	else
	{
		wIntegration = 1;
		scale_factor_w = ((scale_out_w+1) << 16) / scale_in_w;
		if(((scale_out_w+1) << 16) % scale_in_w) ++scale_factor_w;
	}

	if(scale_out_h >= scale_in_h)
	{
		hIntegration = 0;
		scale_factor_h = (scale_in_h << 16) / scale_out_h;
		if((scale_in_h << 16) % scale_out_h) ++scale_factor_h;
	}
	else
	{
		hIntegration = 1;
		scale_factor_h = ((scale_out_h+1) << 16) / scale_in_h;
		if(((scale_out_h+1) << 16) % scale_in_h) ++scale_factor_h;
	}
#endif // #if defined(C_BROWSE_FULL_SCREEN) && C_BROWSE_FULL_SCREEN == CUSTOM_OFF

	if(scale_in_w > scale_out_w)
	{
		h_first = 1;
	}
	else
	{
		h_first = 0;
	}

	drv_l1_pscaler_init(PSCALER_A);
	drv_l1_pscaler_H_first_enable(PSCALER_A, h_first);
	drv_l1_pscaler_integration_mode_set(PSCALER_A, hIntegration, wIntegration);
	drv_l1_pscaler_input_source_set(PSCALER_A, PIPELINE_SCALER_INPUT_SOURCE_DRAM);
	drv_l1_pscaler_input_format_set(PSCALER_A, PIPELINE_SCALER_INPUT_FORMAT_YUYV);

	if(flag)
	{
		drv_l1_pscaler_output_format_set(PSCALER_A, PIPELINE_SCALER_OUTPUT_FORMAT_YUYV);
	}
	else
	{
		if(drv_l2_display_tft_fmt_get() == DISP_FMT_RGB565)
		{
			drv_l1_pscaler_output_format_set(PSCALER_A, PIPELINE_SCALER_OUTPUT_FORMAT_RGB565);
		}
		else if(drv_l2_display_tft_fmt_get() == DISP_FMT_GP420)
		{
			drv_l1_pscaler_output_format_set(PSCALER_A, PIPELINE_SCALER_OUTPUT_FORMAT_GP420);
		}
		else
		{
			drv_l1_pscaler_output_format_set(PSCALER_A, PIPELINE_SCALER_OUTPUT_FORMAT_YUYV);
		}
	}

	drv_l1_pscaler_outboundary_color_set(PSCALER_A, 0, 0x008080);

	switch(fifo_line)
	{
		case 8:
			drv_l1_pscaler_input_fifo_line_set(PSCALER_A, PSCALER_IN_FIFO_MODE_RELOAD_ADDRS, PSCALER_IN_FIFO_8_LINES);
		break;
		case 16:
			drv_l1_pscaler_input_fifo_line_set(PSCALER_A, PSCALER_IN_FIFO_MODE_RELOAD_ADDRS, PSCALER_IN_FIFO_16_LINES);
		break;
		case 32:
			drv_l1_pscaler_input_fifo_line_set(PSCALER_A, PSCALER_IN_FIFO_MODE_RELOAD_ADDRS, PSCALER_IN_FIFO_32_LINES);
		break;
		case 64:
			drv_l1_pscaler_input_fifo_line_set(PSCALER_A, PSCALER_IN_FIFO_MODE_RELOAD_ADDRS, PSCALER_IN_FIFO_64_LINES);
		break;
		case 128:
			drv_l1_pscaler_input_fifo_line_set(PSCALER_A, PSCALER_IN_FIFO_MODE_RELOAD_ADDRS, PSCALER_IN_FIFO_128_LINES);
		break;
		case 256:
			drv_l1_pscaler_input_fifo_line_set(PSCALER_A, PSCALER_IN_FIFO_MODE_RELOAD_ADDRS, PSCALER_IN_FIFO_256_LINES);
		break;
		default:
			drv_l1_pscaler_input_fifo_line_set(PSCALER_A, PSCALER_IN_FIFO_MODE_RELOAD_ADDRS, PSCALER_IN_FIFO_FRAME_MODE);
		break;
	}

	drv_l1_pscaler_pixels_set(
		PSCALER_A
		, scale_in_w
		, scale_in_h
		, scale_out_w
		, scale_out_h
		, scale_factor_w
		, scale_factor_h
	);

	if(outfull_start_x || outfull_start_y)
	{
		drv_l1_pscaler_outfill_set(
			PSCALER_A
			, 1
			, outfull_start_x
			, output_width
			, outfull_start_y
			, output_height
		);
	}

	drv_l1_pscaler_output_A_buffer_set(PSCALER_A, output_addr);
	drv_l1_pscaler_output_fifo_line_set(PSCALER_A, output_height, 0);

	// Start JPEG
	ret = video_decode_jpeg_process(
		(INT32U)data_addr, data_size
		, jpg_temp_out_buf, jpg_temp_out_buf + (img_extend_width * fifo_line << 1)
		, data_reload, reload_size
	);

	if(jpg_temp_out_buf)
	{
		gp_free((void *) jpg_temp_out_buf);
	}

	// STOP JPEG and Scaler
	jpeg_decode_stop();
	drv_l1_pscaler_stop(PSCALER_A);

	return ret;

}
#endif
#if 1
INT32S video_decode_jpeg(
	INT8U *data_addr, INT32U data_size
	, INT32U output_addr, INT32U output_width, INT32U output_height
	, INT32S (*data_reload)(INT32U,INT32U), INT32U reload_size)
{
	INT16U	img_valid_width, img_valid_height, img_extend_width, img_extend_height;
	INT32U	jpg_temp_out_buf;
	INT32S	ret;
	INT16U	i;
	INT32U	fifo_line = 16;
	INT32U	scale_in_w,scale_in_h;
	INT32U	scale_out_w, scale_out_h;
	INT32U	scale_factor_w, scale_factor_h;
	INT16U	outfull_start_x = 0;
	INT16U	outfull_start_y = 0;

	// Find JPEG header(0xFFD8)
	for(i = 0; i < 64; i++)
	{
		if(*data_addr == 0xFF)
		{
			break;
		}
		else
		{
			data_addr++;
		}
	}

	// Parse JPEG data header
	jpeg_decode_init();

	if(jpeg_decode_parse_header(data_addr, data_size) != JPEG_PARSE_OK)
	{
		return STATUS_FAIL;
	}

	img_valid_width = jpeg_decode_image_width_get();
	img_valid_height = jpeg_decode_image_height_get();
	img_extend_width = jpeg_decode_image_extended_width_get();
	img_extend_height = jpeg_decode_image_extended_height_get();

	if(fifo_line)
	{
		jpg_temp_out_buf = (INT32U) gp_malloc_align(img_extend_width * fifo_line << 2, 64);
	}
	else
	{
		jpg_temp_out_buf = (INT32U) gp_malloc_align(img_extend_width * img_extend_height << 1, 64);
	}

	if(!jpg_temp_out_buf)
	{
		DBG_PRINT("%s allocate fail\r\n", __func__);
		return STATUS_FAIL;
	}

	if(jpeg_decode_output_set(jpg_temp_out_buf, 0x0, 0x0, fifo_line))
	{
		gp_free((void *) jpg_temp_out_buf);
		return STATUS_FAIL;
	}

	drv_l1_jpeg_vlc_maximum_length_set(JPEG_DECODE_DEV_NUM, data_size);

	scale_in_w = img_valid_width;
	scale_in_h = img_valid_height;
	scale_out_w = output_width;
	scale_out_h = output_height;

	scale_factor_w = ((scale_in_w << 16) / scale_out_w);
	if((scale_in_w << 16) % scale_out_w) ++scale_factor_w;

	scale_factor_h = ((scale_in_h << 16) / scale_out_h);
	if((scale_in_h << 16) % scale_out_h) ++scale_factor_h;

	#if defined(C_BROWSE_FULL_SCREEN) && C_BROWSE_FULL_SCREEN == CUSTOM_OFF
	/* resize image and relocate to center*/
	if(scale_factor_h < scale_factor_w)
	{
		INT16U tmp_start_h;
		INT16U tmp_width_h;
		INT32U tmp_factor_h;

		tmp_factor_h	= scale_factor_w;
		tmp_width_h		= (scale_in_h << 16) / tmp_factor_h;

		if(tmp_width_h & 0x1)
		{
			tmp_width_h &= ~0x1;

			tmp_factor_h = ((scale_in_h << 16) / tmp_width_h);
			if((scale_in_h << 16) % tmp_width_h) ++tmp_factor_h;
		}

		tmp_start_h = (output_height - tmp_width_h) >> 1;
		tmp_start_h &= ~0x1;

		if(tmp_start_h > (output_height >> 4))
		{
			outfull_start_y	= tmp_start_h;
			scale_factor_h	= tmp_factor_h;
			scale_out_h		= tmp_width_h;

			#if 0
			DBG_PRINT("resize to %dx%d at (%d,%d)\r\n", scale_out_w, scale_out_h, outfull_start_x, outfull_start_y);
			#endif
		}

	}
	else
	if(scale_factor_w < scale_factor_h)
	{
		INT16U	tmp_start_w;
		INT16U	tmp_width_w;
		INT32U	tmp_factor_w;

		tmp_factor_w	= scale_factor_h;
		tmp_width_w		= (scale_in_w << 16) / tmp_factor_w;

		if(tmp_width_w & 0xF)
		{
			tmp_width_w &= ~0xF;

			tmp_factor_w = ((scale_in_w << 16) / tmp_width_w);
			if((scale_in_w << 16) % tmp_width_w) ++tmp_factor_w;
		}

		tmp_start_w = (output_width - tmp_width_w) >> 1;
		tmp_start_w &= ~0xF;

		if(tmp_start_w > (output_width >> 4))
		{
			outfull_start_x	= tmp_start_w;
			scale_factor_w	= tmp_factor_w;
			scale_out_w		= tmp_width_w;

			#if 0
			DBG_PRINT("resize to %dx%d at (%d,%d)\r\n", scale_out_w, scale_out_h, outfull_start_x, outfull_start_y);
			#endif
		}
	}
	#endif // #if defined(C_BROWSE_FULL_SCREEN) && C_BROWSE_FULL_SCREEN == CUSTOM_OFF

	drv_l1_pscaler_init(PSCALER_A);
	drv_l1_pscaler_H_first_enable(PSCALER_A, 1);
	drv_l1_pscaler_input_source_set(PSCALER_A, PIPELINE_SCALER_INPUT_SOURCE_DRAM);
	drv_l1_pscaler_input_format_set(PSCALER_A, PIPELINE_SCALER_INPUT_FORMAT_YUYV);

	if(drv_l2_display_tft_fmt_get() == DISP_FMT_RGB565)
	{
		drv_l1_pscaler_output_format_set(PSCALER_A, PIPELINE_SCALER_OUTPUT_FORMAT_RGB565);
	}
	else if(drv_l2_display_tft_fmt_get() == DISP_FMT_GP420)
	{
		drv_l1_pscaler_output_format_set(PSCALER_A, PIPELINE_SCALER_OUTPUT_FORMAT_GP420);
	}
	else
	{
		drv_l1_pscaler_output_format_set(PSCALER_A, PIPELINE_SCALER_OUTPUT_FORMAT_YUYV);
	}

	drv_l1_pscaler_outboundary_color_set(PSCALER_A, 0, 0x008080);

	switch(fifo_line)
	{
		case 8:
			drv_l1_pscaler_input_fifo_line_set(PSCALER_A, PSCALER_IN_FIFO_MODE_RELOAD_ADDRS, PSCALER_IN_FIFO_8_LINES);
		break;
		case 16:
			drv_l1_pscaler_input_fifo_line_set(PSCALER_A, PSCALER_IN_FIFO_MODE_RELOAD_ADDRS, PSCALER_IN_FIFO_16_LINES);
		break;
		case 32:
			drv_l1_pscaler_input_fifo_line_set(PSCALER_A, PSCALER_IN_FIFO_MODE_RELOAD_ADDRS, PSCALER_IN_FIFO_32_LINES);
		break;
		case 64:
			drv_l1_pscaler_input_fifo_line_set(PSCALER_A, PSCALER_IN_FIFO_MODE_RELOAD_ADDRS, PSCALER_IN_FIFO_64_LINES);
		break;
		case 128:
			drv_l1_pscaler_input_fifo_line_set(PSCALER_A, PSCALER_IN_FIFO_MODE_RELOAD_ADDRS, PSCALER_IN_FIFO_128_LINES);
		break;
		case 256:
			drv_l1_pscaler_input_fifo_line_set(PSCALER_A, PSCALER_IN_FIFO_MODE_RELOAD_ADDRS, PSCALER_IN_FIFO_256_LINES);
		break;
		default:
			drv_l1_pscaler_input_fifo_line_set(PSCALER_A, PSCALER_IN_FIFO_MODE_RELOAD_ADDRS, PSCALER_IN_FIFO_FRAME_MODE);
		break;
	}

	drv_l1_pscaler_pixels_set(
		PSCALER_A
		, scale_in_w
		, scale_in_h
		, scale_out_w
		, scale_out_h
		, scale_factor_w
		, scale_factor_h
	);

	if(outfull_start_x || outfull_start_y)
	{
		drv_l1_pscaler_outfill_set(
			PSCALER_A
			, 1
			, outfull_start_x
			, output_width
			, outfull_start_y
			, output_height
		);
	}

	drv_l1_pscaler_output_A_buffer_set(PSCALER_A, output_addr);
	drv_l1_pscaler_output_fifo_line_set(PSCALER_A, output_height, 0);

	// Start JPEG
	ret = video_decode_jpeg_process(
		(INT32U)data_addr, data_size
		, jpg_temp_out_buf, jpg_temp_out_buf + (img_extend_width * fifo_line << 1)
		, data_reload, reload_size
	);

	if(jpg_temp_out_buf)
	{
		gp_free((void *) jpg_temp_out_buf);
	}

	// STOP JPEG and Scaler
	jpeg_decode_stop();
	drv_l1_pscaler_stop(PSCALER_A);

	return ret;

}
#endif
static INT16S video_decode_jpeg_fd;
INT32S video_decode_jpeg_read_file_reload(INT32U buf_addr, INT32U buf_size)
{
	return read(video_decode_jpeg_fd, buf_addr, buf_size);
}

INT32S video_decode_jpeg_read_file(INT16S fd, INT32U file_size, INT32U output_width, INT32U output_height, INT32U output_addr)
{
	#define JPEG_DEC_PIECE_SIZE 32768

	INT32U	file_buf_addr;
	INT32U	file_buf_size = file_size;
	INT32S	size_read;
	INT8U	*data_addr;
	INT32S	ret;

	if(fd < 0 || !output_width || !output_height || !output_addr)
	{
		return STATUS_FAIL;
	}

	#if JPEG_DEC_PIECE_SIZE
	if(JPEG_DEC_PIECE_SIZE < file_size)
	{
		file_buf_size = JPEG_DEC_PIECE_SIZE;
	}
	#endif

	// allocate buffer to decode
	file_buf_addr = (INT32U) gp_malloc_align(file_buf_size, 64);

	if(!file_buf_addr)
	{
		DBG_PRINT("%s allocate fial\r\n", __func__);
		return STATUS_FAIL;
	}

	video_decode_jpeg_fd = fd;
	size_read = video_decode_jpeg_read_file_reload(file_buf_addr, file_buf_size);

	if(size_read <= 0)
	{
		gp_free((void *) file_buf_addr);
		DBG_PRINT("%s read jpeg file fail.\r\n", __func__);
		return STATUS_FAIL;
	}

	data_addr = (INT8U *) file_buf_addr;

	ret = video_decode_jpeg(
		data_addr, file_size
		, output_addr, output_width, output_height
		, video_decode_jpeg_read_file_reload
		, file_buf_size
	//	, 0
	);

	if(file_buf_addr)
	{
		gp_free((void *) file_buf_addr);
	}

	return ret;
}

